
#pragma once

namespace AVUI {

class IInputElement;

/// <summary>Provides a set of static methods, attached properties, and events for determining and setting focus scopes and for setting the focused element within the scope. </summary>
class FocusManager : public Object
{
public:
    DECLARE_ELEMENT(FocusManager, Object);

#pragma region Public Methods

    /// <summary>Determines the closest ancestor of the specified element that has <see cref="P:System.Windows.Input.FocusManager.IsFocusScope" /> set to true.</summary>
    /// <returns>The focus scope for the specified element.</returns>
    /// <param name="element">The element to get the closest focus scope for.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// <paramref name="element" /> is null.</exception>
    static DependencyObject* GetFocusScope(DependencyObject* pElement);

#pragma endregion Public Methods

#pragma region Public Properties

    DECLARE_DEPENDENCY_PROPERTY(FocusedElement);

    /// <summary>Gets the element with logical focus within the specified focus scope.</summary>
    /// <returns>The element in the specified focus scope with logical focus.</returns>
    /// <param name="element">The element with logical focus in the specified focus scope.</param>
    /// <exception cref="T:System.ArgumentNullException">
    /// <paramref name="element" /> is null.</exception>
    static IInputElement* GetFocusedElement(DependencyObject* pElement);

    /// <summary>Sets logical focus on the specified element.</summary>
    /// <param name="element">The focus scope in which to make the specified element the <see cref="P:System.Windows.Input.FocusManager.FocusedElement" />.</param>
    /// <param name="value">The element to give logical focus to.</param>
    static void SetFocusedElement(DependencyObject* pElement, IInputElement* pValue);

    DECLARE_DEPENDENCY_PROPERTY(IsFocusScope);

    /// <summary>Determines whether the specified <see cref="T:System.Windows.DependencyObject" /> is a focus scope.</summary>
    /// <returns>true if <see cref="P:System.Windows.Input.FocusManager.IsFocusScope" /> is set to true on the specified element; otherwise, false.</returns>
    /// <param name="element">The element from which to read the attached property.</param>
    static bool GetIsFocusScope(DependencyObject* pElement);

    /// <summary>Sets the specified <see cref="T:System.Windows.DependencyObject" /> as a focus scope. </summary>
    /// <param name="element">The element to make a focus scope.</param>
    /// <param name="value">true if <paramref name="element" /> is a focus scope; otherwise, false.</param>
    static void SetIsFocusScope(DependencyObject* pElement, bool value);

#pragma endregion Public Properties

#pragma endregion Public Events

    /// <summary>Identifies the GotFocus<see cref="T:System.Windows.RoutedEvent" />.</summary>
    DECLARE_ROUTED_EVENT(GotFocus);

    /// <summary>Identifies the LostFocus<see cref="T:System.Windows.RoutedEvent" />.</summary>
    DECLARE_ROUTED_EVENT(LostFocus);

#pragma endregion Public Events

private:
    static void StaticConstructor();

#pragma region Private Methods

    static DependencyObject* _GetFocusScope(DependencyObject* pDO);
    static DependencyObject* GetRoot(DependencyObject* pElement);
    static void OnFocusedElementChanged(DependencyObject* pDO, DependencyPropertyChangedEventArgs* pArgs);
 
#pragma endregion Private Methods

};

}; // namespace AVUI


